iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
Software Development

30天學會Golang系列 第 14

Day14 - Go的 channel (下)

  • 分享至 

  • xImage
  •  

channel 介紹

go 的創作者在 channel 這塊有提過一個概念

(原) Don't communicate by sharing memory; sharememory by communicating.
(譯) 不要通過共享內存來通信;應該使用通信來共享內存

因此好的 channel 應該是當訊息傳輸完成後,在傳遞的最後給予一個傳遞完成的資訊,類似對講機結束會說一句 over 的概念,那要怎麼實現這個做法呢,請參考以下程式碼,稍微小修改了一下 day13 的程式碼,還不是好的做法,如果想要看看好作法的話,就請繼續往下看:)

// version 1 需要 TimeSleep 的寫法,則會 print 的結果不完整
func worker1(id int, c chan int) {
	for n := range c {
		fmt.Printf("Worker %d received %c \n", id, n)
	}
}

func createWorker1(id int) chan<- int {
	c := make(chan int)
	go worker1(id, c)
	return c
}

func chanDemo1() {
	var channels [10]chan<- int
	for i := 0; i < 10; i++ {
		channels[i] = createWorker1(i)
	}

	for i := 0; i < 10; i++ {
		channels[i] <- 'a' + i
	}

	for i := 0; i < 10; i++ {
		channels[i] <- 'A' + i
	}

	time.Sleep(time.Millisecond) // 刪除?
}

func main() {
	chanDemo()
}

輸出結果為:

Worker 5 received f 
Worker 2 received c 
Worker 3 received d 
Worker 4 received e 
Worker 0 received a 
Worker 6 received g 
Worker 1 received b 
Worker 6 received G 
Worker 1 received B 
Worker 2 received C 
Worker 3 received D 
Worker 4 received E 
Worker 5 received F 
Worker 8 received i 
Worker 7 received h 
Worker 7 received H 
Worker 8 received I 
Worker 9 received j 
Worker 9 received J 
Worker 0 received A 

將 time.Sleep 刪除的話,就會看到不完整的資料,結果為:

Worker 5 received f 
Worker 2 received c 
Worker 3 received d 
Worker 4 received e 
Worker 0 received a 
Worker 8 received i 
Worker 7 received h 
Worker 9 received j 
Worker 1 received b 
Worker 1 received B 
Worker 5 received F 
Worker 6 received g 
Worker 6 received G 

那接下來提的方式就是結束後多補上一句 over 的程式碼:

// version 2 不需要 TimeSleep
// 但會按照順序打印,表示是單純按照順序執行,沒有發揮 goroutine 的功能
func doWorker2(id int, c chan int, done chan bool) {
	for n := range c {
		fmt.Printf("Worker %d received %c \n", id, n)
		done <- true
	}
}

type worker2 struct {
	in   chan int
	done chan bool
}

func createWorker2(id int) worker2 {
	w := worker2{
		in:   make(chan int),
		done: make(chan bool),
	}
	go doWorker2(id, w.in, w.done)
	return w
}

func chanDemo2() {
	var workers [10]worker2
	for i := 0; i < 10; i++ {
		workers[i] = createWorker2(i)
	}

	for i := 0; i < 10; i++ {
		workers[i].in <- 'a' + i // send
		<-workers[i].done        // receive,主要是因為被這行塞住了,所以導致會按順序執行
	}

	for i := 0; i < 10; i++ {
		workers[i].in <- 'A' + i
		<-workers[i].done
	}
}

輸出的結果為:

Worker 0 received a 
Worker 1 received b 
Worker 2 received c 
Worker 3 received d 
Worker 4 received e 
Worker 5 received f 
Worker 6 received g 
Worker 7 received h 
Worker 8 received i 
Worker 9 received j 
Worker 0 received A 
Worker 1 received B 
Worker 2 received C 
Worker 3 received D 
Worker 4 received E 
Worker 5 received F 
Worker 6 received G 
Worker 7 received H 
Worker 8 received I 
Worker 9 received J 

這個方法不需要 time.Sleep 了,但可以看到上面的結果是有排序的,這樣就表示每個執行緒是按照前面的順序完成才排到下一個,這樣就喪失了 goroutine 的效能,因此我們需要再稍微改善一下:

// version 3 資料蒐集完就 print
func doWorker3(id int, c chan int, done chan bool) {
	for n := range c {
		fmt.Printf("Worker %d received %c \n", id, n)
		done <- true
	}
}

type worker3 struct {
	in   chan int
	done chan bool
}

func createWorker3(id int) worker3 {
	w := worker3{
		in:   make(chan int),
		done: make(chan bool),
	}
	go doWorker3(id, w.in, w.done)
	return w
}

func chanDemo3() {
	var workers [10]worker3
	for i := 0; i < 10; i++ {
		workers[i] = createWorker3(i)
	}

	for i, worker := range workers {
		worker.in <- 'a' + i // send
	}

	for i := 0; i < 10; i++ {
		<-workers[i].done // receive
	}

	for i, worker := range workers {
		worker.in <- 'A' + i
	}

	for i := 0; i < 10; i++ {
		<-workers[i].done // receive
	}
}

輸出結果為:

Worker 0 received a 
Worker 1 received b 
Worker 6 received g 
Worker 9 received j 
Worker 7 received h 
Worker 3 received d 
Worker 8 received i 
Worker 4 received e 
Worker 2 received c 
Worker 5 received f 
Worker 5 received F 
Worker 0 received A 
Worker 4 received E 
Worker 2 received C 
Worker 8 received I 
Worker 1 received B 
Worker 6 received G 
Worker 7 received H 
Worker 9 received J 
Worker 3 received D 

但是這樣感覺還是有點怪,因為還是得大小寫分開,表示還沒辦法將全部資料併行運算,反倒是 version 1 可以,明天看看有沒有比較好的解法,另外 參考來源2 講解得還不錯,可以參考一下

第14天報到,總覺得還是有點困惑啊啊啊啊

參考來源

  1. https://coding.m.imooc.com/classindex.html?cid=180
  2. https://reurl.cc/lepvaQ

代碼連結

https://github.com/luckyuho/ithome30-golang/tree/main/day14


上一篇
Day13 - Go的 channel (上)
下一篇
Day15 - Go的 WaitGroup
系列文
30天學會Golang31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言